package com.slipper.common.util;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.text.DecimalFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.crypto.Cipher;
import javax.crypto.NullCipher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.slipper.common.constants.Constants;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

public final class Tool {
    /**
     * 格式化BigDecimal为字符串
     *
     * @param value 输入的BigDecimal
     * @return 格式化的字符串
     */
    public static String bigDecimalToString(BigDecimal value) {
        String returnVal = value.toString();
        if (returnVal.length() - returnVal.indexOf(".") > 4) {
            returnVal = returnVal.substring(0, returnVal.indexOf(".") + 5);
        } else if (!returnVal.contains(".")) {
            returnVal = returnVal + ".00";
        }
        return returnVal;
    }

    /**
     * 将字符串数组转换成List
     *
     * @param strAry 字符串数组
     * @return List strAry中的元素
     */
    public static <T> List<T> getListFromArray(T[] strAry) {
        List<T> tmpList = new ArrayList<>();
        Collections.addAll(tmpList, strAry);
        return tmpList;
    }

    /**
     * 将字符数组拼凑成字符串
     *
     * @param src       源数组
     * @param splitChar 元素之间的分隔符
     * @param quote     是否用单引号括起元素
     * @return 拼凑成的字符串
     */
    public static <T> String changeArrayToString(T[] src, String splitChar, boolean quote) {
        List<T> tmpList = Tool.getListFromArray(src);
        return Tool.changeArrayToString(tmpList, splitChar, quote);
    }

    /**
     * 将字符数组拼凑成字符串,要去空格
     *
     * @param src       源数组
     * @param splitChar 元素之间的分隔符
     * @param quote     是否用单引号括起元素
     * @return 拼凑成的字符串
     */
    public static <T> String changeArrayToStringTrim(T[] src, String splitChar, boolean quote) {
        List<T> tmpList = Tool.getListFromArray(src);
        return Tool.changeArrayToStringTrim(tmpList, splitChar, quote);
    }

    /**
     * 将列表拼凑成字符串,要去空格
     *
     * @param src       源数组
     * @param splitChar 元素之间的分隔符
     * @param quote     是否用单引号括起元素
     * @return 拼凑成的字符串
     */
    public static String changeArrayToStringTrim(List<?> src, String splitChar, boolean quote) {
        StringBuilder buffer = new StringBuilder();
        Object obj;
        for (int i = 0; i < src.size(); ++i) {
            obj = (src.get(i) == null) ? src.get(i) : src.get(i).toString().trim();
            buffer.append(quote ? "'" : "").append(obj).append(quote ? "'" : "")
                    .append((splitChar != null && i < src.size() - 1) ? splitChar : "");
        }
        return buffer.toString();
    }

    /**
     * 将字符数组拼凑成字符串
     *
     * @param src       源数组
     * @param splitChar 元素之间的分隔符
     * @param quote     是否用单引号括起元素
     * @return 拼凑成的字符串
     */
    public static String changeArrayToString(List<?> src, String splitChar, boolean quote) {
        StringBuilder buffer = new StringBuilder();
        for (int i = 0; i < src.size(); ++i) {
            buffer.append(quote ? "'" : "").append(src.get(i)).append(quote ? "'" : "")
                    .append((splitChar != null && i < src.size() - 1) ? splitChar : "");
        }
        return buffer.toString();
    }

    /**
     * 将对象列表指定的属性拼凑成字符串
     *
     * @param src       对象数组
     * @param property  要组装的对象属性
     * @param splitChar 元素之间的分隔符
     * @param quote     是否用单引号括起元素
     * @return 拼凑成的字符串
     */
    public static String changeArrayToString(List<?> src, String property, String splitChar, boolean quote) {
        StringBuilder buffer = new StringBuilder();
        BeanWrapper infoWrapper;
        for (int i = 0; i < src.size(); ++i) {
            infoWrapper = new BeanWrapperImpl(src.get(i));
            buffer.append(quote ? "'" : "").append(infoWrapper.getPropertyValue(property)).append(quote ? "'" : "")
                    .append((splitChar != null && i < src.size() - 1) ? splitChar : "");
        }
        return buffer.toString();
    }

    /**
     * 将Map指定的属性拼凑成字符串
     *
     * @param src       Map
     * @param splitChar 元素之间的分隔符
     * @param key       是否取Map的key来组装
     * @param quote     是否用单引号括起元素
     * @return 拼凑成的字符串
     */
    public static String changeArrayToString(Map<Object, Object> src, String splitChar, boolean key, boolean quote) {
        StringBuilder buffer = new StringBuilder();
        for (Iterator<Map.Entry<Object, Object>> iter = src.entrySet().iterator(); iter.hasNext(); ) {
            Map.Entry<Object, Object> element = iter.next();
            if (key) {
                buffer.append(quote ? "'" : "").append(element.getKey()).append(quote ? "'" : "")
                        .append((splitChar != null && iter.hasNext()) ? splitChar : "");
            } else {
                buffer.append(quote ? "'" : "").append(element.getValue()).append(quote ? "'" : "")
                        .append((splitChar != null && iter.hasNext()) ? splitChar : "");
            }
        }
        return buffer.toString();
    }

    /**
     * 将以,分割的数字str字符串分成Long形数组返回
     */
    public static Long[] changeStringToLongArray(String str) {
        List<Long> strList = new ArrayList<>();
        String[] strArr = str.split(",");
        for (String s : strArr) {
            if (s != null && !s.isEmpty()) {
                strList.add(Long.valueOf(s));
            }
        }
        return strList.toArray(new Long[0]);
    }

    /**
     * 将以,分割的数字str字符串分成Integer形数组返回
     */
    public static Integer[] changeStringToIntegerArray(String str) {
        List<Integer> strList = new ArrayList<>();
        String[] strArr = str.split(",");
        for (String s : strArr) {
            if (s != null && !s.isEmpty()) {
                strList.add(Integer.valueOf(s));
            }
        }
        return strList.toArray(new Integer[0]);
    }

    /**
     * 将数组转成列表List
     */
    public static <T> List<T> changeArrayToList(T[] objects) {
        List<T> list = new ArrayList<>();
        if (objects != null) {
            Collections.addAll(list, objects);
        }
        return list;
    }

    /**
     * 根据查找内容在数组中找到对应的位置
     *
     * @param searchId    查找内容
     * @param searchArray 数组
     * @return searchId在数组中的位置
     */
    public static int findPosition(Object searchId, Object[] searchArray) {
        Object obj;
        if (searchId == null) {
            return -1;
        }
        for (int i = 0; i < searchArray.length; ++i) {
            obj = searchArray[i];
            if (obj == null) {
                continue;
            }
            if (String.class.isAssignableFrom(obj.getClass())) {
                obj = obj.toString().trim();
            }
            if (obj.equals(searchId)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 判断字符串是否为非空字符串（null和零长度的字符串都是空）
     *
     * @param str 源字符串
     * @return true：非空字符串 false：空字符串
     */
    public static boolean isNotEmpty(String str) {
        return str != null && !str.trim().isEmpty();
    }

    /**
     * 判断list是否为非空列表（null和零长度的字符串都是空）
     *
     * @param list 源列表
     * @return true：非空列表 false：空列表
     */
    public static boolean isNotEmptyList(List<?> list) {
        boolean result = true;
        if (list != null && !list.isEmpty()) {
            return result;
        }
        return false;
    }

    /**
     * 判断字符串是否为非空Map（null和零长度的字符串都是空）
     *
     * @param map 源map
     * @return true：非空map false：空map
     */
    public static boolean isNotEmptyMap(Map<?, ?> map) {
        boolean result = true;
        if (map != null && !map.isEmpty()) {
            return result;
        }
        return false;
    }

    /**
     * 是否为有效的主键ID
     */
    public static boolean isValidId(Long id) {
        return (id != null) && (id > 0);
    }

    /**
     * 去除字符串两边的空格
     */
    public static String trim(String string) {
        return Tool.trim(string, null);
    }

    /**
     * 去除字符串两边的字符
     */
    public static String trim(String string, String str) {
        if (string == null) {
            return null;
        }
        String pattern;
        if (str == null) {
            pattern = "^\\s+|\\s+$";
        } else {
            pattern = "^" + str + "+|" + str + "+$";
        }
        return string.replaceAll(pattern, "");
    }

    /**
     * 分割字符串
     *
     * @param separator    分割字符
     * @param sourceString 需要分割的字符串
     * @return String[]
     */
    public static String[] split(String separator, String sourceString) {
        Pattern p = Pattern.compile(separator);
        return p.split(sourceString);
    }

    /**
     * 清除url上的参数
     */
    public static String cleanUrlParameter(String url) {
        if (url != null && !url.isEmpty()) {
            if (url.indexOf("?") > 0) {
                return Tool.cleanUrlParameter(url.substring(0, url.indexOf("?")));
            } else if (url.indexOf("&") > 0) {
                return Tool.cleanUrlParameter(url.substring(0, url.indexOf("&")));
            } else {
                return url;
            }
        }
        return null;
    }

    /**
     * 获取相对路径
     */
    public static String obtainRelativeUrl(String relativePath, String absolutePath) {
        String url = "";
        if (absolutePath != null && !absolutePath.isEmpty()) {
            if (relativePath != null && relativePath.length() > 1) {
                if (absolutePath.indexOf(relativePath) > 0) {
                    url = absolutePath.substring(absolutePath.indexOf(relativePath) + relativePath.length());
                } else {
                    url = absolutePath;
                }
            } else {
                if (absolutePath.contains("://")) {
                    url = absolutePath.substring(absolutePath.indexOf("://") + "://".length());
                    if (url.contains("/")) {
                        url = url.substring(url.indexOf("/"));
                    }
                }
            }
        }
        if (url.startsWith("/")) {
            url = url.substring(1);
        }
        url = Tool.cleanUrlParameter(url);
        return url;
    }

    /**
     * 清除相同字符串
     */
    public static String cleanSameString(String str, String splitChar) {
        if (str != null && !str.isEmpty()) {
            String[] strs = str.split(splitChar);
            Set<String> set = new TreeSet<>();
            for (String s : strs) {
                if (s != null && !s.isEmpty()) {
                    set.add(s);
                }
            }
            return Tool.changeArrayToString(set.toArray(), splitChar, false);
        }
        return null;
    }

    /**
     * 计算逗号分隔的有效个数
     */
    public static int calcSplitCount(String str) {
        int count = 0;
        if (str != null && !str.isEmpty()) {
            String[] ss = str.split(",");
            for (String s : ss) {
                if (s != null && !s.isEmpty()) {
                    count++;
                }
            }
        }
        return count;
    }

    /**
     * 从左边开始填充字符串至特定长度
     */
    public static String fillLength(String str, int len, char c) {
        if (str != null && !str.isEmpty() && str.length() >= len) {
            return str;
        }
        StringBuilder buf = new StringBuilder();
        for (int i = str == null ? 0 : str.length(); i < len; i++) {
            buf.append(c);
        }
        buf.append(str == null ? "" : str);
        return buf.toString();
    }

    /**
     * 获取URL路径剔除前开始“/”，剔除“?”后的所的参数
     */
    public static String getRelativePath(String url) {
        if (url == null || url.isEmpty()) {
            return null;
        }
        if (url.indexOf("?") > 0) {
            url = url.substring(0, url.indexOf("?"));
        }
        return url;
    }

    /**
     * 获取URL路径剔除前开始“/”，剔除“?”后的所的参数
     */
    public static String getRelativePath(HttpServletRequest request) {
        return Tool.getRelativePath(request.getServletPath());
    }

    public static String getRandomString(int length) {
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder returnStr = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            returnStr.append(str.charAt(random.nextInt(str.length())));
        }
        return returnStr.toString();
    }

    public static String getRandomHexString(int length) {
        String str = "0123456789abcdef";
        StringBuilder returnStr = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            returnStr.append(str.charAt(random.nextInt(str.length())));
        }
        return returnStr.toString();
    }

    public static String getRandomNumber(int length) {
        String str = "0123456789";
        StringBuilder returnStr = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            returnStr.append(str.charAt(random.nextInt(str.length())));
        }
        return returnStr.toString();
    }

    public static int getRandomNumber(int min, int max) {
        int distance = max - min;
        if (distance <= 0) {
            return max;
        }
        return min + new Random().nextInt(distance);
    }

    /**
     * 获取最大数
     */
    public static int max(int... number) {
        int max = 0;
        for (int n : number) {
            max = Math.max(n, max);
        }
        return max;
    }

    /**
     * 获取数组index的值
     */
    public static <T> T getIndexValue(T[] t, int index, T defaultT) {
        if (t.length > index) {
            return t[index];
        }
        return defaultT;
    }

    /**
     * 将 map反转
     */
    public static Map<String, Object> reverseMap(Map<String, Object> map) {
        Map<String, Object> temp = new HashMap<>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            temp.put(entry.getValue() == null ? null : entry.getValue().toString(), entry.getKey());
        }
        return temp;
    }

    /**
     * 类型转换
     */
    public static boolean isBaseType(Type type) {
        if (type == null) {
            return false;
        }
        return type.toString().equals(String.class.toString()) || type.toString().equals(Date.class.toString())
                || type.toString().equals(Byte.class.toString()) || type.toString().equals(byte.class.toString())
                || type.toString().equals(Short.class.toString()) || type.toString().equals(short.class.toString())
                || type.toString().equals(Integer.class.toString()) || type.toString().equals(int.class.toString())
                || type.toString().equals(Long.class.toString()) || type.toString().equals(long.class.toString())
                || type.toString().equals(Float.class.toString()) || type.toString().equals(float.class.toString())
                || type.toString().equals(Double.class.toString()) || type.toString().equals(double.class.toString())
                || type.toString().equals(Boolean.class.toString()) || type.toString().equals(boolean.class.toString())
                || type.toString().equals(Character.class.toString())
                || type.toString().equals(char.class.toString());
    }

    /**
     * 获取IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if ("0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "127.0.0.1";
        }

        if (ip.indexOf(",") > 0) {
            String[] ips = ip.split(",");
            for (String str : ips) {
                if (str != null && !str.trim().isEmpty()) {
                    ip = str.trim();
                    break;
                }
            }
        }
        return ip;
    }

    public static String toString(Object object) {
        if (object == null) {
            return "";
        }

        return object.toString();
    }

    /**
     * 获取终端类型
     */
    public static String getUersAgent(HttpServletRequest request) {
        return request.getHeader("User-Agent");
    }

    /**
     * 处理ie导出excel时编码
     */
    public static void beforeExport(HttpServletResponse response, String fileName) {
        if (fileName == null || fileName.trim().isEmpty()) {
            fileName = "default.xlsx"; // 定义保存的文件名字
        }
        try {
            String trueName = URLEncoder.encode(fileName, Constants.UTF_8);
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + trueName + "\"");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    public static String setTimeCnBySecond(Integer second) {
        if (second != null) {
            int millisecond = second * 1000;
            int days = (int) Math.floor(millisecond * 1.0 / (24 * 3600 * 1000));
            int leave1 = millisecond % (24 * 3600 * 1000);
            int hours = (int) Math.floor(leave1 * 1.0 / (3600 * 1000));
            int leave2 = leave1 % (3600 * 1000);
            int minutes = (int) Math.floor(leave2 * 1.0 / (60 * 1000));
            int leave3 = leave2 % (60 * 1000);
            int seconds = (int) Math.round(leave3 * 1.0 / 1000);
            return days + "天" + hours + "小时" + minutes + "分" + seconds + "秒";
        } else {
            return "";
        }
    }

    /**
     * 如果字符串为null时返回空
     */
    public String filterNull(String value) {
        if (value == null) {
            return "";
        } else {
            return value;
        }
    }

    /**
     * 驼峰式命名转换成数据库命名
     */
    public static String changeName(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            if (Character.isUpperCase(str.charAt(i))) {
                builder.append("_").append(Character.toLowerCase(str.charAt(i)));
            } else {
                builder.append(str.charAt(i));
            }
        }
        return builder.toString();
    }

    public static boolean hasLetter(String str) {
        String regex = ".*[a-zA-Z]+.*";
        Matcher m = Pattern.compile(regex).matcher(str);
        return m.matches();
    }

    /**
     * 只显示前3位，后4位信息
     */
    public static String filterStr(String str) {
        if (str == null || str.length() <= 7) {
            return "*";
        }
        String result = str.substring(0, 3);
        result += Tool.fillLength("", str.length() - 7, '*');
        result += str.substring(str.length() - 4);
        return result;
    }

    public static String formatMoney(Double amount, int len) {
        if (amount == null) {
            return "0";
        }
        return new DecimalFormat("#0." + fillLength("", len, '0')).format(amount);
    }

    /**
     * 将202006格式转换成2020年6月
     */
    public static String formatYm(Integer yearMonth) {
        if (yearMonth == null || yearMonth.toString().length() < 6) {
            return yearMonth == null ? null : yearMonth.toString();
        }

        String yyyyMM = yearMonth.toString();

        String yyyy = yyyyMM.substring(0, 4);
        //System.out.println(yearMonth);
        String mm = yyyyMM.substring(4);
        //System.out.println(mm);
        if (mm.charAt(0) == '0') {
            mm = mm.substring(1);
        }

        return yyyy + "年" + mm + "月";
    }

    public static String formatDate(Integer period) {
        if (period == null || period.toString().length() < 8) {
            return period == null ? null : period.toString();
        }

        String yyyyMMdd = period.toString();

        String yyyy = yyyyMMdd.substring(0, 4);
        String mm = yyyyMMdd.substring(4, 6);
        String dd = yyyyMMdd.substring(6);

        return yyyy + "-" + mm + "-" + dd;
    }

    /**
     * uuid获取，20位UUID，无，11位16进制时间 + 9位随机字条串
     * return UUID.randomUUID().toString().toLowerCase().replaceAll("-", "");
     */
    public static String getUUID() {
        String prefix = fillLength(Long.toHexString(System.currentTimeMillis()), 12, '0');
        return prefix + getRandomHexString(20 - prefix.length());
    }

    /**
     * 公钥解密
     */
    public static byte[] pubDecrypt(byte[] data, String publicKey) throws Exception {
        ECPublicKey pubKey = getEcPublicKey(publicKey);

        Cipher cipher = new NullCipher();
        cipher.init(Cipher.DECRYPT_MODE, pubKey);

        return cipher.doFinal(data);
    }

    // 将base64编码后的公钥字符串转成PublicKey实例
    private static ECPublicKey getEcPublicKey(String publicKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        Base64.Decoder decoder = Base64.getDecoder();
        byte[] keyBytes = decoder.decode(publicKey);

        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("EC");

        return (ECPublicKey) keyFactory.generatePublic(x509KeySpec);
    }

    public static String desensitization(String str, int startLen, int endLen, int totalLen) {
        if (str == null || str.isEmpty()) {
            return null;
        }

        String result = "";
        int starLen = totalLen - startLen - endLen;

        if (str.length() > startLen) {
            result += str.substring(0, startLen);
        }
        if (starLen <= 0) {
            result += "***";
        } else {
            result += fillLength("", starLen, '*');
        }
        int startEnd = str.length() - endLen;
        if (startEnd < 0) {
            startEnd = 0;
        }
        result += str.substring(startEnd);
        return result;
    }

    public static String[] getIpAddrs(HttpServletRequest request) {
        List<String> ipList = new ArrayList<>();
        String ip = request.getHeader("X-Real-IP");
        if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
            ipList.add(ip);
        }
        ip = request.getHeader("X-Forwarded-For");
        if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
            int index = ip.indexOf(',');
            if (index != -1) {
                ipList.add(ip.substring(0, index));
            } else {
                ipList.add(ip);
            }
        }
        ip = request.getHeader("Proxy-Client-IP");
        if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
            ipList.add(ip);
        }
        ip = request.getHeader("WL-Proxy-Client-IP");
        if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
            ipList.add(ip);
        }
        ip = request.getHeader("HTTP_CLIENT_IP");
        if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
            ipList.add(ip);
        }
        ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
            ipList.add(ip);
        }
        ip = request.getRemoteAddr();
        if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
            ipList.add(ip);
        }
        return ipList.toArray(new String[]{});
    }

    public static Map<String, Object> beanToMap(Object obj) throws Exception {
        if (obj == null) {
            return null;
        }

        Map<String, Object> map = new HashMap<>();
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (!key.equals("class")) {
                Method getter = property.getReadMethod();
                if (getter.getParameterCount() <= 0) {
                    Object value = getter.invoke(obj);
                    map.put(key, value);
                }
            }
        }
        return map;
    }

    public static void main(String[] args) {
        System.out.println(getUUID());
        System.out.println(getUUID());
        System.out.println(getUUID());
        System.out.println(getUUID());
        System.out.println(getUUID());
        System.out.println(getUUID());
        System.out.println(getUUID());
    }
}
